package org.pwwong.scalautils.signals

import java.util.{TimerTask,Timer}
import javax.swing.SwingUtilities

import org.pwwong.scalautils.time._

import scala.concurrent.SyncVar

object TimedSignal{
  def apply(res:Duration) = new TimedSignal[Any](res,null){
    override def onStart = null
    override def onCycle = null
  }
  def apply(res:Duration, startImmediately:Boolean) = {
    val rtn = new TimedSignal[Any](res, null){
      override def onStart = null
      override def onCycle = null
    }
    rtn.start
    rtn
  }
  protected val timer = new Timer(true)
}

abstract class TimedSignal[T](val resolution:Duration, initVal:T) extends Signal[T]{
  private val cycleResult = new SyncVar[T]();
  cycleResult set initVal
  
  private val _isRunning = new SyncVar[Boolean]();
  _isRunning.set(false)
  
  private val _isPause = new SyncVar[Boolean]();
  _isPause.set(false)
  
  protected def onStart:T
  protected def onCycle:T
  protected def onStop {}
  
  class TimedSignalTask extends TimerTask {
    private def _onCycle() {
      // Pause
      if(!_isPause.get){
        cycleResult set onCycle
        SwingUtilities.invokeLater( new Runnable(){ val value = cycleResult.get
                                                    override def run() = onChange.fire(value)
        })
      }
    }
    private def _onStart() { _isRunning set true; onStart; _run = _onCycle }
    private var _run:()=>Unit = _onStart
    
    override def cancel:Boolean = {
      _isRunning.set(false)
      onStop
      super.cancel
    }
    override def run = _run()
  }
  private var curTimerTask:TimedSignalTask = null
  
  def start {
    if(!_isRunning.get){
      curTimerTask = new TimedSignalTask
      TimedSignal.timer.scheduleAtFixedRate(curTimerTask, resolution.to_ms, resolution.to_ms)
    }
  }
  def pause {
    _isPause set false
  }
  def stop {
    if(_isRunning.get && curTimerTask != null){
      _isRunning set false
      curTimerTask.cancel
    }
  }
  
  final def isRunning:Boolean = _isRunning.get
  final def isPaused:Boolean = _isPause.get
  
  override def apply():T = cycleResult.get
}

